package com.hqyj.springbootday03.modules.account.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.hqyj.springbootday03.modules.account.dao.UserDao;
import com.hqyj.springbootday03.modules.account.dao.UserRoleDao;
import com.hqyj.springbootday03.modules.account.entity.User;
import com.hqyj.springbootday03.modules.account.entity.UserRole;
import com.hqyj.springbootday03.modules.account.service.UserService;
import com.hqyj.springbootday03.modules.common.vo.Result;
import com.hqyj.springbootday03.modules.common.vo.Search;
import com.hqyj.springbootday03.utils.MD5Util;
import com.hqyj.springbootday03.utils.RedisUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.Optional;

/**
 * UserServiceImpl
 */
@Service
public class UserServiceImpl implements UserService {
	public final static Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);
	@Autowired
	private UserDao userDao;
	@Autowired
	private UserRoleDao userRoleDao;
	@Autowired
	private RedisUtils redisUtils;

	@Override
	@Transactional
	public Result<User> login(User user) {
//		User temp = userDao.getUserByUserNameAndPassword(user.getUserName(), MD5Util.getMD5(user.getPassword()));
//		if (temp == null) {
//			return new Result<>(Result.ResultStatus.FAILED.code, "用户名或密码错误。");
//		}
//		return new Result<>(Result.ResultStatus.SUCCESS.code, "Success.", temp);

		/**
		 * 检查是否连续错误超过最大错误次数 3；
		 * 是否在 30 秒之内
		 * 如果满足上面两个条件，直接返回 result
		 */
		// 最大错误次数
		int maxError = 3;
		String errorKey = String.format("login_error_count_%s", user.getUserName());
		// 错误次数
		int errorCount = redisUtils.get(errorKey)  == null ? 0 : (Integer) redisUtils.get(errorKey);
		if (errorCount >= maxError) {
			return new Result<>(Result.ResultStatus.FAILED.code,
					String.format("用户连续错误登录三次，锁定账户 30 秒。"));
		}

		Subject subject = SecurityUtils.getSubject();
		// 登录令牌
		UsernamePasswordToken token =
				new UsernamePasswordToken(user.getUserName(), MD5Util.getMD5(user.getPassword()));

		User temp = null;
		try {
			subject.login(token);
			subject.checkRoles();

			temp = (User) subject.getPrincipal();
		} catch (Exception e) {
			e.printStackTrace();
			LOGGER.debug(e.getMessage());
			/**
			 *  当输入错误时候，错误的次数累加 1
			 *  判断错误次数是否超过最大值
			 *  如果没超过，提示错误几次，还可以继续输入
			 *  如果超过，设置 30 秒过期时间，并提示锁定账户 30 秒
			 */
			redisUtils.increment(errorKey, 1);
			errorCount = redisUtils.get(errorKey)  == null ? 0 : (Integer) redisUtils.get(errorKey);
			if (errorCount >= maxError) {
				redisUtils.expire(errorKey, 30);
				return new Result<>(Result.ResultStatus.FAILED.code,
						String.format("用户连续登录错误 %d 次，锁住账户 30 秒。", errorCount));
			} else  {
				return new Result<>(Result.ResultStatus.FAILED.code,
						String.format("用户连续登录错误 %d 次，还剩 %d 次机会。", errorCount, (maxError - errorCount)));
			}
		}

		return new Result<>(Result.ResultStatus.SUCCESS.code, "Success.", temp);
	}

	@Override
	@Transactional
	public Result<User> insertUser(User user) {
		User temp = userDao.getUserByUserName(user.getUserName());
		if (temp != null) {
			return new Result<>(Result.ResultStatus.FAILED.code, "用户名重复");
		}

		user.setCreateDate(LocalDateTime.now());
		user.setPassword(MD5Util.getMD5(user.getPassword()));
		userDao.insertUser(user);
		if (user.getRoles() != null && user.getRoles().size() > 0) {
			user.getRoles().stream().forEach(item -> {
				userRoleDao.insertUserRole(new UserRole(user.getId(), item.getId()));
			});
		}

		return new Result<>(Result.ResultStatus.SUCCESS.code, "Insert success.", user);
	}

	@Override
	@Transactional
	public Result<User> updateUser(User user) {
		User temp = userDao.getUserByUserName(user.getUserName());
		if (temp != null && temp.getId() != user.getId()) {
			return new Result<>(Result.ResultStatus.FAILED.code, "用户名重复");
		}

		userDao.updateUser(user);
		userRoleDao.deleteUserRolesByUserId(user.getId());
		if (user.getRoles() != null && user.getRoles().size() > 0) {
			user.getRoles().stream().forEach(item -> {
				userRoleDao.insertUserRole(new UserRole(user.getId(), item.getId()));
			});
		}

		return new Result<>(Result.ResultStatus.SUCCESS.code, "Update success.", user);
	}

	@Override
	@Transactional
	public Result<Object> deleteUserById(int id) {
		userDao.deleteUserById(id);
		return new Result<>(Result.ResultStatus.SUCCESS.code, "Delete success.");
	}

	@Override
	public User getUserById(int id) {
		return userDao.getUserById(id);
	}

	@Override
	public User getUserByUserName(String userName) {
		return userDao.getUserByUserName(userName);
	}

	@Override
	public PageInfo<User> getUsersBySearch(Search search) {
		search.init();
		PageHelper.startPage(search.getCurrentPage(), search.getPageSize());
		return new PageInfo<>(Optional
				.ofNullable(userDao.getUsersBySearch(search))
				.orElse(Collections.emptyList()));
	}

	@Override
	public void logout() {
		Subject subject = SecurityUtils.getSubject();
		subject.logout();
	}
}
